package org.jeecg.util;

import org.jeecg.common.api.vo.Result;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class CommonUtil {

    private static final char[] DIGITS_LOWER =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private final static String CHARSET = "UTF-8";
    public static Integer TIMESTAMP =  Integer.parseInt(Long.toString(System.currentTimeMillis()/1000));//获取当前Unix时间戳
    public static Integer NONCE = (int)(Math.random()*100000000)+1;//随机正整数
    /**
     *上传
     * @param file 视频文件
     * @param savePaths 上传的地址
     * @param map 可为null,可传一些必要的值带到前端
     * @return
     * @throws IllegalStateException
     */
    public static Result<?> uploadVideo(MultipartFile file, String savePaths, Map map)
            throws IllegalStateException {

        if(StringUtils.isEmpty(savePaths)){
            return  Result.error("上传的地址不能为空！") ;
        }
        try{
            //获取文件后缀
            String fileExt = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1)
                    .toLowerCase();
            // 重构文件名称
            String pikId = UUID.randomUUID().toString().replaceAll("-", "");
            String newVidoeName = pikId + "." + fileExt;
            //保存视频
            File fileSave = new File(savePaths, newVidoeName);
            file.transferTo(fileSave);
            return  Result.ok(map);
        }catch (Exception e){
            e.printStackTrace();
            return  Result.error("上传视频失败！") ;
        }
    }

    /**
     * 检查用户有没有权限
     * @param roleMap
     * @return
     */
    public static boolean checkUserRole(Map roleMap,String str){

        if(roleMap.isEmpty()){
            return false;
        }
        Iterator<Map.Entry<String, String>> it = roleMap.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<String, String> entry = it.next();
            if(entry.getValue().equals(str)){
                return true;
            }
        }
        return false;
    }

    /*
     * KEY+ streamName + txTime 生成直播url鉴权key
     */
    public static String getSafeUrl(String key, String streamName, String txTime) {
        txTime = txTime.toUpperCase();
        String input = new StringBuilder().
                append(key).
                append(streamName).
                append(txTime).toString();

        String txSecret = null;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            txSecret  = byteArrayToHexString(
                    messageDigest.digest(input.getBytes("UTF-8")));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return txSecret == null ? "" :
                new StringBuilder().
                        append("txSecret=").
                        append(txSecret).
                        append("&").
                        append("txTime=").
                        append(txTime).
                        toString();
    }

    public static String byteArrayToHexString(byte[] data) {
        char[] out = new char[data.length << 1];

        for (int i = 0, j = 0; i < data.length; i++) {
            out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
            out[j++] = DIGITS_LOWER[0x0F & data[i]];
        }
        return new String(out);
    }


    public static String sign(String s, String key, String method) throws Exception {
        Mac mac = Mac.getInstance(method);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(CHARSET), mac.getAlgorithm());
        mac.init(secretKeySpec);
        byte[] hash = mac.doFinal(s.getBytes(CHARSET));
        return DatatypeConverter.printBase64Binary(hash);
    }

    public static String getStringToSign(TreeMap<String, Object> params,String reqType,String apiType) {
        StringBuilder s2s = new StringBuilder(reqType+apiType+"/?");// 示例 GETlive.tencentcloudapi.com/?
        // 签名时要求对参数进行字典排序，此处用TreeMap保证顺序
        for (String k : params.keySet()) {
            s2s.append(k).append("=").append(params.get(k).toString()).append("&");
        }
        return s2s.toString().substring(0, s2s.length() - 1);
    }

    public static String getUrl(TreeMap<String, Object> params,String apiType) throws UnsupportedEncodingException {
        StringBuilder url = new StringBuilder("https://"+apiType+"/?");//示例 https://live.tencentcloudapi.com/?
        // 实际请求的url中对参数顺序没有要求
        for (String k : params.keySet()) {
            // 需要对请求串进行urlencode，由于key都是英文字母，故此处仅对其value进行urlencode
            url.append(k).append("=").append(URLEncoder.encode(params.get(k).toString(), CHARSET)).append("&");
        }
        return url.toString().substring(0, url.length() - 1);
    }

    /**
     * 直播流管理 查询直播中的流  生成V1签名
     * @param action  操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。
     * @param secretId  在云API密钥上申请的标识身份的 SecretId，一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature
     * @param version  操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明
     * @param domain  非必填 推流域名
     * @param appName  非必填 推流路径
     * @param pageNum  非必填 取得第几页，默认1。
     * @param pageSize 非必填 每页大小，最大100。取值：10~100之间的任意整数。 默认值：10
     * @param streamName  非必填 流名称，用于精确查询 可为null
     * @param reqType   请求方法: 支持 POST 和 GET 方式，注意方法为全大写。
     * @param apiType  接口请求域名
     * @param secretKey  一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名
     * @return
     * @throws Exception
     */
    public static String queryingStreamsLive(String action,String secretId,String version,String domain,String appName,
                                             Integer pageNum,Integer pageSize,String streamName,String reqType,String apiType,
                                             String secretKey) throws Exception {

        // TreeMap可以自动排序
        TreeMap<String, Object> params  = setCommonParametLiveV1(action,secretId,version);

        if(!StringUtils.isEmpty(domain)){
            // 非必填 推流域名。多域名用户需要填写 DomainName。
            params.put("DomainName", domain);
        }
        if(!StringUtils.isEmpty(appName)){
            // 非必填 推流路径，与推流和播放地址中的 AppName 保持一致，默认为 live。多路径用户需要填写 AppName。
            params.put("AppName", appName);
        }
        //非必填 取得第几页，默认1。
        params.put("PageNum",pageNum);
        //非必填 每页大小，最大100。取值：10~100之间的任意整数。 默认值：10
        params.put("PageSize",pageSize);
        if(!StringUtils.isEmpty(streamName)){
            //非必填 流名称，用于精确查询
            params.put("StreamName",streamName);
        }
        params.put("Signature", sign(getStringToSign(params,reqType,apiType), secretKey, "HmacSHA1")); // 公共参数
//        System.out.println(getUrl(params,apiType));
        return getUrl(params,apiType);
    }


    /**
     * 直播流管理 断开直播流
     * @param action   必填  操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。
     * @param secretId  必填 在云API密钥上申请的标识身份的 SecretId，一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature
     * @param version   必填 操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明
     * @param domainName  必填 您的推流域名
     * @param appName    必填 推流路径，与推流和播放地址中的AppName保持一致，默认为 live。
     * @param streamName  必填 流名称
     * @param reqType   请求方法: 支持 POST 和 GET 方式，注意方法为全大写。
     * @param apiType  接口请求域名  示例 https://live.tencentcloudapi.com/?
     * @param secretKey  一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名
     * @return
     * @throws Exception
     */
    public static String disconnectLiveStream(String action,String secretId,String version,String domainName,
                                              String appName,String streamName,String reqType,String apiType,
                                              String secretKey) throws Exception {

        // TreeMap可以自动排序
        TreeMap<String, Object> params  = setCommonParametLiveV1(action,secretId,version);
        params.put("DomainName", domainName);
        params.put("AppName", appName);
        params.put("StreamName",streamName);
        params.put("Signature", sign(getStringToSign(params,reqType,apiType), secretKey, "HmacSHA1")); // 公共参数
        return getUrl(params,apiType);
    }


    /**
     * 直播流管理 禁推直播流
     * @param action   必填  操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。
     * @param secretId  必填 在云API密钥上申请的标识身份的 SecretId，一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature
     * @param version   必填 操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明
     * @param domainName  必填 您的推流域名
     * @param appName    必填 推流路径，与推流和播放地址中的AppName保持一致，默认为 live。
     * @param streamName  必填 流名称
     * @param reqType   请求方法: 支持 POST 和 GET 方式，注意方法为全大写。
     * @param apiType  接口请求域名  示例 https://live.tencentcloudapi.com/?
     * @param secretKey  一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名
     * @return
     * @throws Exception
     */
    public static String getDescribeLiveStreamStateAction(String action,String secretId,String version,String domainName,
                                                          String appName,String streamName,String reqType,String apiType,
                                                          String secretKey) throws Exception {

        // TreeMap可以自动排序
        TreeMap<String, Object> params  = setCommonParametLiveV1(action,secretId,version);
        params.put("DomainName", domainName);
        params.put("AppName", appName);
        params.put("StreamName",streamName);
        params.put("Signature", sign(getStringToSign(params,reqType,apiType), secretKey, "HmacSHA1")); // 公共参数
        return getUrl(params,apiType);
    }


    /**
     * 直播流管理 禁推直播流
     * @param action   必填  操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。
     * @param secretId  必填 在云API密钥上申请的标识身份的 SecretId，一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature
     * @param version   必填 操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明
     * @param domainName  必填 您的推流域名
     * @param appName    必填 推流路径，与推流和播放地址中的AppName保持一致，默认为 live。
     * @param streamName  必填 流名称
     * @param reqType   请求方法: 支持 POST 和 GET 方式，注意方法为全大写。
     * @param apiType  接口请求域名  示例 https://live.tencentcloudapi.com/?
     * @param secretKey  一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名
     * @return
     * @throws Exception
     */
    public static String forbidLiveStream(String action,String secretId,String version,String domainName,
                                          String appName,String streamName,String reqType,String apiType,
                                          String secretKey) throws Exception {

        // TreeMap可以自动排序
        TreeMap<String, Object> params  = setCommonParametLiveV1(action,secretId,version);
        params.put("DomainName", domainName);
        params.put("AppName", appName);
        params.put("StreamName",streamName);
        params.put("Signature", sign(getStringToSign(params,reqType,apiType), secretKey, "HmacSHA1")); // 公共参数
        return getUrl(params,apiType);
    }


    /**
     * 直播流管理 查询流的播放信息列表
     * @param action   必填  操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。
     * @param secretId  必填 在云API密钥上申请的标识身份的 SecretId，一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature
     * @param version   必填 操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明
     * @param startTime  必填 开始时间，北京时间，格式为yyyy-mm-dd HH:MM:SS，当前时间 和 开始时间 间隔不超过30天
     * @param endTime  结束时间，北京时间，格式为yyyy-mm-dd HH:MM:SS，结束时间 和 开始时间 必须在同一天内。
     * @param playDomain  播放域名 若不填，则为查询所有播放域名的在线流数据。
     * @param appName    必填 推流路径，与推流和播放地址中的AppName保持一致，默认为 live。
     * @param streamName  必填 流名称
     * @param reqType   请求方法: 支持 POST 和 GET 方式，注意方法为全大写。
     * @param apiType  接口请求域名  示例 https://live.tencentcloudapi.com/?
     * @param secretKey  一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名
     * @return
     * @throws Exception
     */
    public static String queryStreamPlayInfoList(String action,String secretId,String version,String startTime,
                                                 String endTime,String playDomain,String streamName,String appName,
                                                 String reqType,String apiType, String secretKey) throws Exception {

        // TreeMap可以自动排序
        TreeMap<String, Object> params  = setCommonParametLiveV1(action,secretId,version);

        params.put("StartTime", startTime);
        params.put("EndTime", endTime);
        if(!StringUtils.isEmpty(playDomain)){
            //播放域名，
            //若不填，则为查询所有播放域名的在线流数据。
            params.put("PlayDomain", playDomain);
        }
        if(!StringUtils.isEmpty(streamName)){
            //非必填 流名称，用于精确查询
            params.put("StreamName",streamName);
        }

        if(!StringUtils.isEmpty(appName)){
            // 推流路径，与播放地址中的AppName保持一致，会精确匹配，在同时传递了StreamName时生效。
            //若不填，则为查询总体播放数据。
            //注意：按AppName查询，需要联系客服同学提单支持。
            params.put("AppName", appName);
        }

        params.put("Signature", sign(getStringToSign(params,reqType,apiType), secretKey, "HmacSHA1")); // 公共参数
//        System.out.println(getUrl(params,apiType));
        return getUrl(params,apiType);
    }


    /**
     * 直播流管理 查询流的播放信息列表
     * @param action   必填  操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。
     * @param secretId  必填 在云API密钥上申请的标识身份的 SecretId，一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature
     * @param version   必填 操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明
     * @param appName    必填 推流路径，与推流和播放地址中的AppName保持一致，默认为 live。
     * @param domainName  您的推流域名
     * @param streamName  必填 流名称
     * @param reqType   请求方法: 支持 POST 和 GET 方式，注意方法为全大写。
     * @param apiType  接口请求域名  示例 https://live.tencentcloudapi.com/?
     * @param secretKey  一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名
     * @return
     * @throws Exception
     */
    public static String resumeLiveStreaming(String action,String secretId,String version,
                                             String domainName,String streamName,String appName,
                                             String reqType,String apiType, String secretKey) throws Exception {

        // TreeMap可以自动排序
        TreeMap<String, Object> params  = setCommonParametLiveV1(action,secretId,version);

        params.put("AppName", appName);
        params.put("DomainName", domainName);
        params.put("StreamName",streamName);

        params.put("Signature", sign(getStringToSign(params,reqType,apiType), secretKey, "HmacSHA1")); // 公共参数
//        System.out.println(getUrl(params,apiType));
        return getUrl(params,apiType);
    }


    /**
     * 统计查询相关接口 查询某个时间点所有流的下行播放数据
     * @param action   必填  操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。
     * @param secretId  必填 在云API密钥上申请的标识身份的 SecretId，一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature
     * @param version   必填 操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明
     * @param queryTime  查询时间点，精确到分钟粒度，支持最近1个月的数据查询，数据延迟为5分钟左右，如果要查询实时的数据，建议传递5分钟前的时间点，格式为yyyy-mm-dd HH:MM:SS。
     * @param reqType   请求方法: 支持 POST 和 GET 方式，注意方法为全大写。
     * @param apiType  接口请求域名  示例 https://live.tencentcloudapi.com/?
     * @param secretKey  一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名
     * @return
     * @throws Exception
     */
    public static String queryAllStreamsDataPointTime(String action,String secretId,String version,String queryTime,
                                                      String reqType,String apiType, String secretKey) throws Exception {

        // TreeMap可以自动排序
        TreeMap<String, Object> params  = setCommonParametLiveV1(action,secretId,version);

        params.put("QueryTime", queryTime);

        params.put("Signature", sign(getStringToSign(params,reqType,apiType), secretKey, "HmacSHA1")); // 公共参数
//        System.out.println(getUrl(params,apiType));
        return getUrl(params,apiType);
    }

    /**
     * 设置签名方法 v1 必填的公共参数
     * @param action  操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。
     * @param secretId  在云API密钥上申请的标识身份的 SecretId，一个 SecretId 对应唯一的 SecretKey ，而 SecretKey 会用来生成请求签名 Signature
     * @param version  操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明
     * @return
     * @throws Exception
     */
    public static TreeMap<String, Object> setCommonParametLiveV1(String action,String secretId,String version) {
        //设置必填的公共参数
        TreeMap<String, Object> params = new TreeMap<String, Object>(); // TreeMap可以自动排序

        params.put("Action", action);
        params.put("Timestamp", Integer.parseInt(Long.toString(System.currentTimeMillis()/1000))); // 公共参数 当前 UNIX 时间戳，可记录发起 API 请求的时间。例如1529223702，如果与当前时间相差过大，会引起签名过期错误
        params.put("Nonce", new Random().nextInt(Integer.MAX_VALUE)); // 公共参数 随机正整数，与 Timestamp 联合起来，用于防止重放攻击
        params.put("SecretId",secretId);
        params.put("Version", version);
        return params;
    }


}
